package com.njcb.ams.scheduler.service;

import com.google.common.collect.Sets;
import com.njcb.ams.assembler.SysTaskJobConvertor;
import com.njcb.ams.factory.domain.AppContext;
import com.njcb.ams.pojo.enumvalue.TriggerType;
import com.njcb.ams.repository.dao.SysTaskJobDAO;
import com.njcb.ams.repository.dao.SysTaskJobLogDAO;
import com.njcb.ams.repository.entity.SysTaskJob;
import com.njcb.ams.repository.entity.SysTaskJobLog;
import com.njcb.ams.scheduler.bean.BaseJobDetail;
import com.njcb.ams.scheduler.bean.ScheduleJob;
import com.njcb.ams.scheduler.utils.JobConstant;
import com.njcb.ams.service.SysTaskJobService;
import com.njcb.ams.support.exception.ExceptionUtil;
import com.njcb.ams.util.AmsUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 总的调度服务
 *
 * @author LOONG
 */
@Service
public class AmsJobSchedulerManager {
    private static final Logger logger = LoggerFactory.getLogger(AmsJobSchedulerManager.class);
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    @Autowired
    private SysTaskJobDAO sysTaskJobDAO;
    @Autowired
    private SysTaskJobLogDAO sysTaskJobLogDAO;
    @Autowired
    private SysTaskJobService taskJobService;

    public static AmsJobSchedulerManager getInstance() {
        return AppContext.getBean(AmsJobSchedulerManager.class);
    }

    /**
     * 初始化所有任务,根据SysTaskJob初始化任务
     *
     * @throws Exception
     */
    public void initJobs() throws Exception {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        List<SysTaskJob> allJob = sysTaskJobDAO.getAllJobForUpdate();
        Map<String, SysTaskJob> allJobMap = new HashMap<String, SysTaskJob>(20);
        Set<String> allJobName = new HashSet<String>();
        Set<JobKey> allExistJob = getAllJobKeys();
        Set<String> allExistJobName = new HashSet<String>();
        for (JobKey jobKey : allExistJob) {
            allExistJobName.add(jobKey.getName());
        }
        for (SysTaskJob taskJob : allJob) {
            try {
                @SuppressWarnings("unchecked")
                Class<? extends BaseJobDetail> jobClass = (Class<? extends BaseJobDetail>) Class.forName(taskJob.getProcessFunction());
                if (!BaseJobDetail.class.isAssignableFrom(jobClass)) {
                    ExceptionUtil.throwAppException("调度类" + jobClass.getName() + "必须是继承自BaseJobDetail");
                }
                taskJobService.checkTaskJob(taskJob);
            } catch (ClassNotFoundException e) {
                logger.error("任务调度配置错误,错误信息[" + e.getMessage() + "]", e);
                continue;
            }
            allJobName.add(String.valueOf(taskJob.getId()));
            allJobMap.put(String.valueOf(taskJob.getId()), taskJob);
        }
        // 调度器存在, SysTaskJob不存在的任务
        Set<String> deleteJob = Sets.difference(allExistJobName, allJobName);
        // SysTaskJob存在 , 调度器不存在的任务
        Set<String> addJob = Sets.difference(allJobName, allExistJobName);
        // SysTaskJob调度器都存在的任务
        Set<String> updateJob = Sets.intersection(allExistJobName, allJobName);

        // 删除任务
        for (String jobName : deleteJob) {
            deleteJob(jobName, JobConstant.GROUP_NAME);
        }
        // 新增任务
        for (String jobName : addJob) {
            SysTaskJob taskJob = allJobMap.get(jobName);
            ScheduleJob job = SysTaskJobConvertor.initScheduleJob(null, taskJob);
            addJob(String.valueOf(job.getJobId()), job.getJobGroup(), job);
        }
        // 更新任务
        for (String jobName : updateJob) {
            SysTaskJob taskJob = allJobMap.get(jobName);
            ScheduleJob job = SysTaskJobConvertor.initScheduleJob(null, taskJob);
            updateJob(String.valueOf(job.getJobId()), job.getJobGroup(), job);
        }
        // 启动
        if (!scheduler.isShutdown()) {
            scheduler.start();
        }
    }

    /**
     * 获取实际任务KEY
     *
     * @return
     * @throws SchedulerException
     */
    public Set<JobKey> getAllJobKeys() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        return jobKeys;
    }


    /**
     * 计划中的任务
     * 指那些已经添加到quartz调度器的任务，因为quartz并没有直接提供这样的查询接口，所以我们需要结合JobKey和Trigger来实现，
     */
    public List<ScheduleJob> getAllJob() {
        List<SysTaskJob> allJob = sysTaskJobDAO.selectAll();
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            for (SysTaskJob taskJob : allJob) {
                ScheduleJob job = new ScheduleJob();
                job.setJobId(taskJob.getId());
                job.setJobName(taskJob.getJobName());
                job.setTriggerType(taskJob.getTriggerType());
                job.setDesc(taskJob.getRemark());
                job.setCronExpression(taskJob.getCronExpression());
                for (JobKey jobKey : jobKeys) {
                    if (jobKey.getName().equals(String.valueOf(taskJob.getId()))) {
                        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                        for (Trigger trigger : triggers) {
                            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                            if (trigger instanceof SimpleTriggerImpl && triggerState == Trigger.TriggerState.COMPLETE) {
                                continue;
                            }
                            Date nextTime = trigger.getFireTimeAfter(new Date());
                            Date lastTime = trigger.getPreviousFireTime();
                            job.setJobStatus(triggerState.name());
                            job.setJobGroup(jobKey.getGroup());
                            job.setNextTime(nextTime);
                            job.setLastTime(lastTime);
                        }
                    }
                }
                jobList.add(job);
            }
        } catch (Exception e) {
            ExceptionUtil.throwAppException(e);
        }
        return jobList;
    }


    /**
     * 新增任务
     *
     * @throws Exception
     * @throws SchedulerException
     */
    @SuppressWarnings("unchecked")
    public void addJob(String jobName, String jobGroup, ScheduleJob scheduleJob) throws Exception {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        // 任务名，任务组，任务执行类
        Class<? extends BaseJobDetail> jobClass = (Class<? extends BaseJobDetail>) Class.forName(scheduleJob.getClassName());

        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).storeDurably(true).requestRecovery(true).build();
        jobDetail.getJobDataMap().putAll(scheduleJob.getJobParam());
        jobDetail.getJobDataMap().put(JobConstant.JOBBEAN_KEY, scheduleJob);
        Trigger trigger = createTrigger(jobName, jobGroup, scheduleJob);
        scheduler.scheduleJob(jobDetail, trigger);

    }

    /**
     * 暂停任务
     *
     * @throws SchedulerException
     */
    public void stopJob(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复任务
     *
     * @throws SchedulerException
     */
    public void reJob(String jobName, String jobGroup, ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(String.valueOf(scheduleJob.getJobId()), scheduleJob.getJobGroup());
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除任务
     *
     * @throws SchedulerException
     */
    public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup());
        scheduler.pauseTrigger(triggerKey);
        scheduler.unscheduleJob(triggerKey);
        scheduler.deleteJob(jobKey);
    }

    /**
     * 立即运行任务
     *
     * @throws SchedulerException
     */
    public void triggerJob(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新任务
     *
     * @throws SchedulerException
     */
    public void updateJob(String jobName, String jobGroup, ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        jobDetail.getJobDataMap().putAll(scheduleJob.getJobParam());
        jobDetail.getJobDataMap().put(JobConstant.JOBBEAN_KEY, scheduleJob);
        scheduler.addJob(jobDetail, true);
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        // 获取trigger，即在spring配置文件中定义的 bean id="myTrigger"
        Trigger trigger = scheduler.getTrigger(triggerKey);
        if (AmsUtils.isNull(trigger)) {
            scheduler.pauseTrigger(triggerKey);
            scheduler.unscheduleJob(triggerKey);
            scheduler.deleteJob(jobKey);
            trigger = createTrigger(jobName, jobGroup, scheduleJob);
            scheduler.scheduleJob(jobDetail, trigger);
        }else{
            trigger = createTrigger(jobName, jobGroup, scheduleJob);
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    public List<SysTaskJobLog> jobLogQuery(SysTaskJobLog sysTaskJobLog) {
        return sysTaskJobLogDAO.selectBySelective(sysTaskJobLog);
    }

    private Trigger createTrigger(String jobName, String jobGroup, ScheduleJob scheduleJob){
        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
        if (TriggerType.CRON.getCode().equals(scheduleJob.getTriggerType())) {
            triggerBuilder.withIdentity(jobName, jobGroup);
            triggerBuilder.startNow();
            // 触发器时间设定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()));
            // 创建Trigger对象
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();
            // 调度容器设置JobDetail和Trigger
            return trigger;
        }else if(TriggerType.REPEAT.getCode().equals(scheduleJob.getTriggerType())){
            triggerBuilder.withIdentity(jobName, jobGroup);
            if(AmsUtils.isNotNull(scheduleJob.getStartTime())){
                triggerBuilder.startAt(scheduleJob.getStartTime());
            }
            if(AmsUtils.isNotNull(scheduleJob.getEndTime())){
                triggerBuilder.endAt(scheduleJob.getEndTime());
            }
            // 触发器时间设定
            triggerBuilder.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(scheduleJob.getRepeatCount(),scheduleJob.getRepeatInterval()));
            Trigger trigger =triggerBuilder.build();
            // 调度容器设置JobDetail和Trigger
            return trigger;
        }else{
            ExceptionUtil.throwAppException("任务[" + scheduleJob.getJobName() + "]等触发类型[" + scheduleJob.getTriggerType() + "]非有效枚举");
        }
        return null;
    }
}
